/****************************************************************************
 * libwiigui
 *
 * Tantric 2009
 *
 * gui_window.cpp
 *
 * GUI class definitions
 ***************************************************************************/

#include "gui.h"
#include "../Themes/CTheme.h"

GuiWindow::GuiWindow()
{
	width = 0;
	height = 0;
	forceDim = false;
	allowDim = true;
}

GuiWindow::GuiWindow(int w, int h)
{
	width = w;
	height = h;
	forceDim = false;
	allowDim = true;
}

GuiWindow::~GuiWindow()
{
}

void GuiWindow::Append(GuiElement* e)
{
	LOCK( this );
	if (e == NULL) return;

	Remove(e);
	_elements.push_back(e);
	e->SetParent(this);
}

void GuiWindow::Insert(GuiElement* e, u32 index)
{
	LOCK( this );
	if (e == NULL || index > (_elements.size() - 1)) return;

	Remove(e);
	_elements.insert(_elements.begin() + index, e);
	e->SetParent(this);
}

void GuiWindow::Remove(GuiElement* e)
{
	LOCK( this );
	if (e == NULL) return;

	for (u8 i = 0; i < _elements.size(); i++)
	{
		if (e == _elements.at(i))
		{
			_elements.erase(_elements.begin() + i);
			break;
		}
	}
}

void GuiWindow::RemoveAll()
{
	LOCK( this );
	_elements.clear();
}

GuiElement* GuiWindow::GetGuiElementAt(u32 index) const
{
	if (index >= _elements.size()) return NULL;
	return _elements.at(index);
}

u32 GuiWindow::GetSize()
{
	return _elements.size();
}

void GuiWindow::Draw()
{
	LOCK( this );
	if (_elements.empty() || !this->IsVisible()) return;

	for (u8 i = 0; i < _elements.size(); i++)
	{
		try
		{
			_elements.at(i)->Draw();
		}
		catch (const std::exception& e)
		{
		}
	}

	this->UpdateEffects();

	if ((parentElement && state == STATE_DISABLED && allowDim) || forceDim)
		Menu_DrawRectangle(0, 0, screenwidth, screenheight, thColor("r=0 g=0 b=0 a=112 - disabled mask color"), 1);
}
void GuiWindow::DrawTooltip()
{
	LOCK( this );
	if (_elements.empty() || !this->IsVisible()) return;

	for (u8 i = 0; i < _elements.size(); i++)
	{
		try
		{
			_elements.at(i)->DrawTooltip();
		}
		catch (const std::exception& e)
		{
		}
	}
}
void GuiWindow::ResetState()
{
	LOCK( this );
	if (state != STATE_DISABLED) state = STATE_DEFAULT;

	for (u8 i = 0; i < _elements.size(); i++)
	{
		try
		{
			_elements.at(i)->ResetState();
		}
		catch (const std::exception& e)
		{
		}
	}
}

void GuiWindow::SetState(int s)
{
	LOCK( this );
	state = s;

	for (u8 i = 0; i < _elements.size(); i++)
	{
		try
		{
			_elements.at(i)->SetState(s);
		}
		catch (const std::exception& e)
		{
		}
	}
}

void GuiWindow::SetVisible(bool v)
{
	LOCK( this );
	visible = v;

	for (u8 i = 0; i < _elements.size(); i++)
	{
		try
		{
			_elements.at(i)->SetVisible(v);
		}
		catch (const std::exception& e)
		{
		}
	}
}

int GuiWindow::GetSelected()
{
	// find selected element
	int found = -1;
	for (u8 i = 0; i < _elements.size(); i++)
	{
		try
		{
			if (_elements.at(i)->GetState() == STATE_SELECTED)
			{
				found = i;
				break;
			}
		}
		catch (const std::exception& e)
		{
		}
	}
	return found;
}

// set element to left/right as selected
// there's probably a more clever way to do this, but this way works
void GuiWindow::MoveSelectionHor(int dir)
{
	LOCK( this );
	int found = -1;
	u16 left = 0;
	u16 top = 0;
	u8 i = 0;

	int selected = this->GetSelected();

	if (selected >= 0)
	{
		left = _elements.at(selected)->GetLeft();
		top = _elements.at(selected)->GetTop();
	}

	// look for a button on the same row, to the left/right
	for (i = 0; i < _elements.size(); i++)
	{
		try
		{
			if (_elements.at(i)->IsSelectable())
			{
				if (_elements.at(i)->GetLeft() * dir > left * dir && _elements.at(i)->GetTop() == top)
				{
					if (found == -1)
						found = i;
					else if (_elements.at(i)->GetLeft() * dir < _elements.at(found)->GetLeft() * dir) found = i; // this is a better match
				}
			}
		}
		catch (const std::exception& e)
		{
		}
	}
	if (found >= 0) goto matchfound;

	// match still not found, let's try the first button in the next row
	for (i = 0; i < _elements.size(); i++)
	{
		try
		{
			if (_elements.at(i)->IsSelectable())
			{
				if (_elements.at(i)->GetTop() * dir > top * dir)
				{
					if (found == -1)
						found = i;
					else if (_elements.at(i)->GetTop() * dir < _elements.at(found)->GetTop() * dir)
						found = i; // this is a better match
					else if (_elements.at(i)->GetTop() * dir == _elements.at(found)->GetTop() * dir
							&& _elements.at(i)->GetLeft() * dir < _elements.at(found)->GetLeft() * dir) found = i; // this is a better match
				}
			}
		}
		catch (const std::exception& e)
		{
		}
	}

	// match found
	matchfound: if (found >= 0)
	{
		_elements.at(found)->SetState(STATE_SELECTED);
		if (selected >= 0) _elements.at(selected)->ResetState();
	}
}

void GuiWindow::MoveSelectionVert(int dir)
{
	LOCK( this );
	int found = -1;
	u16 left = 0;
	u16 top = 0;
	u8 i = 0;

	int selected = this->GetSelected();

	if (selected >= 0)
	{
		left = _elements.at(selected)->GetLeft();
		top = _elements.at(selected)->GetTop();
	}

	// look for a button above/below, with the least horizontal difference
	for (i = 0; i < _elements.size(); i++)
	{
		try
		{
			if (_elements.at(i)->IsSelectable())
			{
				if (_elements.at(i)->GetTop() * dir > top * dir)
				{
					if (found == -1)
						found = i;
					else if (_elements.at(i)->GetTop() * dir < _elements.at(found)->GetTop() * dir)
						found = i; // this is a better match
					else if (_elements.at(i)->GetTop() * dir == _elements.at(found)->GetTop() * dir && abs(
							_elements.at(i)->GetLeft() - left) < abs(_elements.at(found)->GetLeft() - left)) found = i;
				}
			}
		}
		catch (const std::exception& e)
		{
		}
	}
	if (found >= 0) goto matchfound;

	// match found
	matchfound: if (found >= 0)
	{
		_elements.at(found)->SetState(STATE_SELECTED);
		if (selected >= 0) _elements.at(selected)->ResetState();
	}
}

void GuiWindow::Update(GuiTrigger * t)
{
	LOCK( this );
	if (_elements.empty() || (state == STATE_DISABLED && parentElement)) return;

	for (u8 i = 0; i < _elements.size(); i++)
	{
		try
		{
			_elements.at(i)->Update(t);
		}
		catch (const std::exception& e)
		{
		}
	}

	if (updateCB) updateCB(this);
}
